﻿using System;
using System.Net;
using System.Net.Sockets;
using Fadd.Logging;

namespace Fadd.Net
{
    /// <summary>
    /// Accept new sockets asynchronously
    /// </summary>
    /// <example>
    /// <code>
    /// <![CDATA[
    /// SimpleServer server 
    ///		= new SimpleServer(
    ///			new IpEndPoint(IpAddress.LoopBack, 933), 
    ///			s => new BinaryChannel<Request>(s));
    /// ]]>
    /// </code>
    /// </example>
    public class SimpleServer
    {
        private readonly Action<Socket> _handler;
        private readonly TcpListener _listener;
        private readonly ILogger _logger = LogManager.GetLogger<SimpleServer>();
        private Action<Exception> _exceptionHandler;

        /// <summary>
        /// Initializes a new instance of the <see cref="SimpleServer"/> class.
        /// </summary>
        /// <param name="endPoint">End point to listen on.</param>
        /// <param name="handler">Handler taking care of incoming connection/clients.</param>
        public SimpleServer(IPEndPoint endPoint, Action<Socket> handler)
        {
            _listener = new TcpListener(endPoint);
            _handler = handler;
            _exceptionHandler = NullExceptionHandler;
        }

        /// <summary>
        /// Gets or sets delegate used to handle exceptions.
        /// </summary>
        public Action<Exception> ExceptionHandler
        {
            get { return _exceptionHandler; }
            set { _exceptionHandler = value ?? NullExceptionHandler; }
        }

        private void NullExceptionHandler(Exception exception)
        {
            _logger.Error("Got exception.", exception);
        }

        private void OnAccept(IAsyncResult ar)
        {
            Socket socket;

            try
            {
                socket = _listener.EndAcceptSocket(ar);
                _handler(socket);
            }
            catch (Exception err)
            {
                ExceptionHandler(err);
            }

            

            try
            {
                _listener.BeginAcceptSocket(OnAccept, null);
            }
            catch (Exception err)
            {
                ExceptionHandler(err);
                Stop();
            }
        }

        /// <summary>
        /// Start server
        /// </summary>
        /// <param name="backLog">Number of pending accepts.</param>
        /// <exception cref="SocketException">Failed to start and listen on socket.</exception>
        public void Start(int backLog)
        {
            _listener.Start(backLog);
            _listener.BeginAcceptSocket(OnAccept, null);
        }

        /// <summary>
        /// Stop server.
        /// </summary>
        /// <exception cref="SocketException">Failed to stop listener.</exception>
        public void Stop()
        {
            _listener.Stop();
        }
    }
}